// directives/autoScroll.ts
import { DirectiveBinding, ObjectDirective } from "vue";

interface AutoScrollOptions {
  speed?: number;
  hoverPause?: boolean;
  maxRetryCount?: number;
  retryInterval?: number;
}

interface AutoScrollElement extends HTMLElement {
  _autoScroll?: {
    animationId: number | null;
    isScrolling: boolean;
    scrollPosition: number;
    scrollHeight: number;
    options: AutoScrollOptions;
    scrollHandler: (timestamp: number) => void;
    mouseEnterHandler?: (this: HTMLElement, ev: MouseEvent) => void;
    mouseLeaveHandler?: (this: HTMLElement, ev: MouseEvent) => void;
    lastTimestamp?: number;
    observer?: MutationObserver;
    retryCount?: number;
    retryTimeout?: number;
    isTitle?: boolean;
    tw?: number;
  };
}

const AutoScrollDirective: ObjectDirective<AutoScrollElement, AutoScrollOptions | undefined> = {
  mounted(el, binding) {
    const options = {
      speed: 30,
      hoverPause: true,
      maxRetryCount: 10,
      retryInterval: 500,
      tw: 200,
      isTitle: false,
      targetFPS: 60, // 新增目标帧率
      ...binding.value,
    };

    const initAutoScroll = () => {
      const tbody = el.querySelector("tbody");

      if (!tbody) {
        if (el._autoScroll && (el._autoScroll.retryCount || 0) < options.maxRetryCount) {
          el._autoScroll.retryCount = (el._autoScroll.retryCount || 0) + 1;
          el._autoScroll.retryTimeout = window.setTimeout(initAutoScroll, options.retryInterval);
        }
        return;
      }

      if (el._autoScroll?.retryTimeout) {
        clearTimeout(el._autoScroll.retryTimeout);
      }

      if (tbody.scrollHeight <= 0) {
        const observer = new MutationObserver(() => {
          if (tbody.scrollHeight > 0) {
            observer.disconnect();
            setupAutoScroll(tbody);
          }
        });

        observer.observe(tbody, {
          childList: true,
          subtree: true,
          attributes: true,
          characterData: true,
        });

        if (el._autoScroll) {
          el._autoScroll.observer = observer;
        }
        return;
      }

      setupAutoScroll(tbody);
    };

    const setupAutoScroll = (tbody: HTMLTableSectionElement) => {
      const rows = tbody.querySelectorAll("tr");
      if (rows.length === 0 || options.tw >= tbody.scrollHeight) return;

      // 使用 DocumentFragment 优化克隆性能
      const fragment = document.createDocumentFragment();
      Array.from(rows).forEach((row) => {
        fragment.appendChild(row.cloneNode(true));
      });
      tbody.appendChild(fragment);

      const scrollHeight = tbody.scrollHeight / 2;
      let scrollPosition = 0;
      let animationId: number | null = null;
      let isScrolling = true;

      const targetElement = options.isTitle ? el.querySelector("table") : tbody;
      if (!targetElement) {
        console.error("AutoScroll: Target element not found");
        return;
      }
      targetElement.style.willChange = "transform";
      targetElement.style.transform = "translateZ(0)";

      const frameInterval = 1000 / (options.targetFPS || 60);
      let lastFrameTime = 0;

      const scrollHandler = (timestamp: number) => {
        if (!el._autoScroll?.isScrolling) return;

        const elapsed = timestamp - lastFrameTime;
        if (elapsed < frameInterval) {
          el._autoScroll.animationId = requestAnimationFrame(scrollHandler);
          return;
        }

        lastFrameTime = timestamp - (elapsed % frameInterval);

        scrollPosition += (options.speed * frameInterval) / 1000;
        const transformValue = `translateY(-${scrollPosition % scrollHeight}px)`;
        targetElement.style.transform = transformValue;

        el._autoScroll.animationId = requestAnimationFrame(scrollHandler);
      };

      el._autoScroll = {
        animationId: null,
        isScrolling: true,
        scrollPosition,
        scrollHeight,
        options,
        scrollHandler,
        retryCount: 0,
      };

      animationId = requestAnimationFrame(scrollHandler);
      el._autoScroll.animationId = animationId;

      if (options.hoverPause) {
        const mouseEnterHandler = () => {
          if (el._autoScroll) {
            el._autoScroll.isScrolling = false;
            if (el._autoScroll.animationId) {
              cancelAnimationFrame(el._autoScroll.animationId);
              el._autoScroll.animationId = null;
            }
          }
        };

        const mouseLeaveHandler = () => {
          if (el._autoScroll) {
            el._autoScroll.isScrolling = true;
            lastFrameTime = 0;
            if (!el._autoScroll.animationId) {
              el._autoScroll.animationId = requestAnimationFrame(scrollHandler);
            }
          }
        };

        el.addEventListener("mouseenter", mouseEnterHandler);
        el.addEventListener("mouseleave", mouseLeaveHandler);

        el._autoScroll.mouseEnterHandler = mouseEnterHandler;
        el._autoScroll.mouseLeaveHandler = mouseLeaveHandler;
      }
    };

    el._autoScroll = {
      animationId: null,
      isScrolling: false,
      scrollPosition: 0,
      scrollHeight: 0,
      options,
      scrollHandler: () => {},
      retryCount: 0,
    };

    initAutoScroll();
  },
  beforeUnmount(el) {
    if (!el._autoScroll) return;

    if (el._autoScroll.observer) {
      el._autoScroll.observer.disconnect();
    }

    if (el._autoScroll.retryTimeout) {
      clearTimeout(el._autoScroll.retryTimeout);
    }

    if (el._autoScroll.animationId) {
      cancelAnimationFrame(el._autoScroll.animationId);
    }

    if (el._autoScroll.mouseEnterHandler) {
      el.removeEventListener("mouseenter", el._autoScroll.mouseEnterHandler);
    }
    if (el._autoScroll.mouseLeaveHandler) {
      el.removeEventListener("mouseleave", el._autoScroll.mouseLeaveHandler);
    }

    delete el._autoScroll;
  },
};
export default AutoScrollDirective;
